#include "fpga.h"
#include "procfs.c"
#include "sysfs.c"
//#include "sam9_smc.h"
//#define AT91_SMC




void sam9_smc_configure(int cs, struct sam9_smc_config* config)
{
	/* Setup register */
	at91_sys_write(AT91_SMC1_SETUP(cs),
		  AT91_SMC_NWESETUP_(config->nwe_setup)
		| AT91_SMC_NCS_WRSETUP_(config->ncs_write_setup)
		| AT91_SMC_NRDSETUP_(config->nrd_setup)
		| AT91_SMC_NCS_RDSETUP_(config->ncs_read_setup)
	);

	/* Pulse register */
	at91_sys_write(AT91_SMC1_PULSE(cs),
		  AT91_SMC_NWEPULSE_(config->nwe_pulse)
		| AT91_SMC_NCS_WRPULSE_(config->ncs_write_pulse)
                | AT91_SMC_NRDPULSE_(config->nrd_pulse)
		| AT91_SMC_NCS_RDPULSE_(config->ncs_read_pulse)
	);

	/* Cycle register */
	at91_sys_write(AT91_SMC1_CYCLE(cs),
		  AT91_SMC_NWECYCLE_(config->write_cycle)
		| AT91_SMC_NRDCYCLE_(config->read_cycle)
	);

	/* Mode register */
	at91_sys_write(AT91_SMC1_MODE(cs),
		  config->mode
		| AT91_SMC_TDF_(config->tdf_cycles)
	);
}




dev_t my_dev=0;
struct cdev * my_cdev = NULL;
static struct class *class_my_fpga = NULL;
//static addresses_handler fpga_add;

static void	* EBI1_virt_address;

void write16(unsigned int address,  unsigned short val)
{
	iowrite16(val,EBI1_virt_address + SHIFT_LEFT(address));

	#ifdef __DEBUG__
	printk(KERN_ALERT " DEBUG: write16(0x%x, 0x%x) \n",(unsigned int)EBI1_virt_address + SHIFT_LEFT(address), (unsigned int)val);
	#endif
}

void read16(unsigned int address,  unsigned short *val)
{

	*val = ioread16(EBI1_virt_address + SHIFT_LEFT(address));			
	#ifdef __DEBUG__
	printk(KERN_ALERT "DEBUG: read16(0x%x, 0x%x)\n",(unsigned int)EBI1_virt_address + SHIFT_LEFT(address), *val);
	#endif
}


void write32(unsigned int address,  unsigned int val)
{


	iowrite16((0xFFFF & val),EBI1_virt_address + SHIFT_LEFT(address));
	iowrite16((0xFFFF &(val >> 16)),EBI1_virt_address + SHIFT_LEFT(address + 0x10));
	#ifdef __DEBUG__
	printk(KERN_ALERT " DEBUG: write32(0x%x, 0x%x) \n",(unsigned int)EBI1_virt_address + SHIFT_LEFT(address), (unsigned int)val);
	#endif
}

void read32(unsigned int address,  unsigned int *val)
{

	unsigned short temp;
	
	*val = ioread16(EBI1_virt_address + SHIFT_LEFT(address));			
	temp = ioread16(EBI1_virt_address + SHIFT_LEFT(address + 0x10));	
	*val = *val + (temp << 16);		
	#ifdef __DEBUG__
	printk(KERN_ALERT "DEBUG: read32(0x%x, 0x%x)\n",(unsigned int)EBI1_virt_address + SHIFT_LEFT(address), *val);
	#endif
}






static long fpga_drv_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
data_16 io_data_16;
data_32 io_data_32;
int err;

switch(cmd) {

	  case FPGA_DRV_READ_16BIT:
		#ifdef __DEBUG__
		printk("INF: ioctl read16 comming\n");
		#endif
		err = copy_from_user (&io_data_16, (void *) arg, sizeof (data_16));
		#ifdef __DEBUG__
		//printk("address from uisr:0x%x\n",io_data_16.address);
		#endif
		read16(io_data_16.address, &io_data_16.data);
		err = copy_to_user ((void*) arg, &io_data_16, sizeof (data_16));
		break;


	case FPGA_DRV_WRITE_16BIT:
		#ifdef __DEBUG__
		printk("INF: ioctl write16 comming\n");
		#endif		
		err = copy_from_user (&io_data_16, (void *) arg, sizeof (data_16));	
		write16(io_data_16.address, io_data_16.data);
		break;

	case FPGA_DRV_RESET:
		#ifdef __DEBUG__
		printk("INF: ioctl reset comming\n");
		#endif			
		write16(0x0, 0x01);
		break;

	case FPGA_DRV_READ_32BIT:
		#ifdef __DEBUG__
		printk("INF: ioctl read32 comming\n");
		#endif
		err = copy_from_user (&io_data_32, (void *) arg, sizeof (data_32));
		read32(io_data_32.address, &io_data_32.data);
		err = copy_to_user ((void*) arg, &io_data_32, sizeof (data_32));
		break;


	case FPGA_DRV_WRITE_32BIT:
		#ifdef __DEBUG__
		printk("INF: ioctl write32 comming\n");

		#endif		
		err = copy_from_user (&io_data_32, (void *) arg, sizeof (data_32));
		//printk("address from uisr:0x%x\n",io_data_32.address);	
		write32(io_data_32.address, io_data_32.data);
		break;



	  default: 
		return -ENOTTY;
	}






return 0;
}

void fpga_vma_open (struct vm_area_struct * area)
{  }

void fpga_vma_close (struct vm_area_struct * area)
{  }



static int fpga_mmap(struct file *filp, struct vm_area_struct *vma)
{
  unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
  unsigned long physical = EBI1_PHYS_ADDR + off;
  unsigned long vsize = vma->vm_end - vma->vm_start;
  unsigned long psize = FPGA_SIZE - off;
  if(vsize>psize)
    return -EINVAL;
  remap_pfn_range(vma,vma->vm_start, physical >> PAGE_SHIFT , vsize, vma->vm_page_prot);
  if (vma->vm_ops)
    return -EINVAL; 
  vma->vm_ops = &fpga_vm_ops;
  fpga_vma_open(vma); 
  return 0;
}








static int fpga_drv_open(struct inode *inode, struct file *file)
{
#ifdef __DEBUG__
//	printk("device_open()\n");
#endif


	try_module_get(THIS_MODULE);
	return 0;
}


static int fpga_drv_release(struct inode *inode, struct file *file)
{
#ifdef __DEBUG__
//	printk(KERN_INFO "device_release()\n");
#endif


	module_put(THIS_MODULE);
	return 0;
}







static int fpga_init(void)
{

//	unsigned int data;
	int ret_val;

	class_my_fpga = class_create(THIS_MODULE, "my_fpga");
	if (IS_ERR(class_my_fpga)) {
		printk(KERN_ERR "ERR: Error creating class.\n");
		ret_val=PTR_ERR(class_my_fpga);
		goto err1;
	}

	ret_val=alloc_chrdev_region(&my_dev, 0, 1, DEVICE_NAME);
	if(ret_val) {
		printk ("ERR: Alocation of the device number for %s failed\n",DEVICE_NAME);
		goto err1; 
	};

	my_cdev = cdev_alloc( );
	if(my_cdev == NULL) {
		printk ("<1>Allocation of cdev for %s failed\n",DEVICE_NAME);
   		 goto err1;
	}
	my_cdev->ops = &fops;
	my_cdev->owner = THIS_MODULE;

	ret_val=cdev_add(my_cdev, my_dev, 1);
	if(ret_val) {
		printk ("ERR: Registration of the device number for %s failed\n",DEVICE_NAME);
		goto err1;
	};

	device_create(class_my_fpga,NULL,my_dev,NULL,"my_fpga%d",MINOR(my_dev));
	printk ("ERR: Registration succesfull. Major number :%d, Minor number: %d\n",MAJOR(my_dev),MINOR(my_dev));


	at91_set_B_periph(AT91_PIN_PB10, 1); //periphb on pb10 enabling
	at91_sys_write(AT91_PMC_PCKR(1), AT91_PMC_CSS_PLLA | AT91_PMC_PRES_2); //clock source
	at91_sys_write(AT91_PMC_SCER, AT91_PMC_PCK1); //enabling pck

	sam9_smc_configure(0, &smc_config);

	if(!request_mem_region(EBI1_PHYS_ADDR,FPGA_SIZE,"FPGA_ebi1")) //requesting mem for ebi
	{
		printk("ERR: ERROR_EBI1: Memory region busy - request rejected\n");
	}


	EBI1_virt_address=ioremap_nocache(EBI1_PHYS_ADDR,FPGA_SIZE);
	if(!EBI1_virt_address)
	{
		printk("ERR: ERROR: Could not map to physical addres: 0x%x\n",EBI1_PHYS_ADDR);
	}
	#ifdef __DEBUG__
	printk("EBI1 virt address: 0x%x\n",(unsigned int)EBI1_virt_address);
	#endif
/*
	write16( 0x0, 0x1); //reset
	write32(0x40,0x66233313);
	read32(0xD0, &data);
	read32(0x40,&data);
*/

	procfs_register();
	sysfs_register();

	return 0;
	
	err1:

 		fpga_exit();
		return 0;

}


 
static void fpga_exit(void)
{

	if(my_dev && class_my_fpga) {
		device_destroy(class_my_fpga,my_dev);
	}
	if(my_cdev) cdev_del(my_cdev);
	my_cdev=NULL;
	unregister_chrdev_region(my_dev, 1);
	if(class_my_fpga) {
		class_destroy(class_my_fpga);
	class_my_fpga=NULL;
  	}
/*	iounmap(fpga_add.PMC);
	iounmap(fpga_add.PIOE);
	iounmap(fpga_add.PIOD);
	iounmap(fpga_add.PIOC);
	iounmap(fpga_add.PIOB);
	iounmap(fpga_add.PIOA);
	release_mem_region(AT91_BASE_SYS + AT91_PIOA,AT91_RSTC-AT91_PIOA);
	iounmap(fpga_add.SMC1);
	release_mem_region(AT91_BASE_SYS + AT91_SMC1,AT91_MATRIX-AT91_SMC1);   */
	iounmap(EBI1_virt_address);  
	release_mem_region(EBI1_PHYS_ADDR,FPGA_SIZE); //releasing ebi1
	procfs_unregister();
	sysfs_unregister();
	printk(KERN_ALERT "remove ok\n");
									
}
 
module_init(fpga_init);
module_exit(fpga_exit);
